package com.samuel.basics.aop;

import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.ui.ModelMap;
import org.springframework.validation.support.BindingAwareModelMap;

import java.lang.reflect.Field;
import java.util.Arrays;

/**
 * <p>
 *     非json时候处理LocalDateTime返回样式
 * </p>
 *
 * @author kaiji
 * @date 2020-01-19 10:35:00
 */
@Aspect
@Order(1)
@Configuration
public class LocalDateTimeNotJsonAop {

	private static final Logger logger = LoggerFactory.getLogger(LocalDateTimeNotJsonAop.class);
	private final String ENTITY_SUFFIX = "Entity";
	private final String EXP_GET_RESULT_DATA_POINT = "execution(* com.samuel.basics.controller..*.*(..))";
		
	/**
	 * 后置返回通知
	 * 这里需要注意的是:
	 * 如果参数中的第一个参数为JoinPoint，则第二个参数为返回值的信息
	 * 如果参数中的第一个参数不为JoinPoint，则第一个参数为returning中对应的参数
	 * returning 限定了只有目标方法返回值与通知方法相应参数类型时才能执行后置返回通知，
	 * 否则不执行，对于returning对应的通知方法参数为Object类型将匹配任何目标返回值
	 */
	@AfterReturning(value = EXP_GET_RESULT_DATA_POINT)
	public void doAfterReturningAdvice(JoinPoint point) {
		Object[] args = point.getArgs();

		Arrays.stream(args)
				.filter(arg -> arg instanceof BindingAwareModelMap)
				.map(arg -> (ModelMap) arg)
				.forEach(this::getClazz);
	}

	private void getClazz(ModelMap modelMap) {
		modelMap.forEach((key, value)-> {
			if (key.endsWith(ENTITY_SUFFIX)) {
				try {
					Class<?> clazz = value.getClass();
					Class<?> superclass = clazz.getSuperclass();
					logger.info("===============解析父类===============");
					parseField(superclass, value);
					logger.info("===============解析子类===============");
					parseField(clazz, value);
				} catch (IllegalAccessException | NoSuchFieldException e) {
					e.printStackTrace();
				}
			}

		});
	}

	/**
	 * 反射解析实体类字段
	 *
	 * @param clazz
	 * 		视图类全部的字段
	 * @param value
	 * 		原始参数对象
	 */
	private void parseField(Class<?> clazz, Object value) throws IllegalAccessException, NoSuchFieldException {

		for (Field field: clazz.getDeclaredFields()) {
			if (StringUtils.equals(field.getType().getSimpleName(), "LocalDateTime")) {
				changeValue(field);
//				field.setAccessible(true);
				// LocalDateTime o = (LocalDateTime)field.get(value);

				// field.set(value, DateUtil.date2Str(o));
//				logger.info("o ====> {}", "666");
			}
		}
	}

	private void changeValue(Field field) {
		field.setAccessible(true);
	}
}
